package com.app.action.concurrent;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolExecutorMotion {

    private final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private final int CORE_CPU_SIZE = CPU_COUNT + 1;
    private final int MAX_CPU_COUNT = CORE_CPU_SIZE * 2 + 1;
    private final int KEEP_ALIVE_SIZE = 1;

    private LinkedBlockingQueue<Runnable> mBlockQueue = new LinkedBlockingQueue<>(128);
    private AtomicInteger mThreadCount = new AtomicInteger(1);
    private ThreadFactory mThreadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "Thread #" + mThreadCount.getAndIncrement());
        }
    };
    public ThreadPoolExecutor sDefaultExecutor = new ThreadPoolExecutor(CORE_CPU_SIZE, MAX_CPU_COUNT,
            KEEP_ALIVE_SIZE, TimeUnit.SECONDS, mBlockQueue, mThreadFactory);

    public static void main(String[] args) {

        ThreadPoolExecutorMotion motion = new ThreadPoolExecutorMotion();
        Random random = new Random();
        SeriableExctuor myExecutor = new SeriableExctuor(motion.sDefaultExecutor);
        for (int i = 0; i < 10; i++) {
            myExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(random.nextInt(10) * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":hello world");
                }
            });
        }
    }

}

class SeriableExctuor implements Executor {

    private Queue<Runnable> tasks = new ArrayDeque<>();
    private Executor executor;
    private Runnable mActive;

    public SeriableExctuor(Executor executor) {
        this.executor = executor;
    }

    @Override
    public void execute(Runnable command) {
        tasks.add(new Runnable() {
            @Override
            public void run() {
                try {
                    command.run();
                } finally {
                    scheduleNext();
                }
            }
        });

        if (mActive == null) {
            scheduleNext();
        }
    }

    private void scheduleNext() {
        if ((mActive = tasks.poll()) != null) {
            executor.execute(mActive);
        }
    }
}
